package com.ku.kill.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.ku.kill.dao.OrderInfoDOMapper;
import com.ku.kill.dao.SequenceInfoDOMapper;
import com.ku.kill.dao.StockLogDOMapper;
import com.ku.kill.dataobject.OrderInfoDO;
import com.ku.kill.dataobject.SequenceInfoDO;
import com.ku.kill.dataobject.StockLogDO;
import com.ku.kill.error.BusinessException;
import com.ku.kill.error.EmBusinessError;
import com.ku.kill.mq.MQProvider;
import com.ku.kill.service.ItemService;
import com.ku.kill.service.OrderService;
import com.ku.kill.service.UserService;
import com.ku.kill.service.model.ItemModel;
import com.ku.kill.service.model.OrderModel;
import com.ku.kill.service.model.UserModel;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;

/**
 * @program: kill2
 * @description: 订单业务类
 * @author: JokerKu
 * @create: 2020-04-03 12:03
 **/
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Autowired
    private ItemService itemService;
    @Autowired
    private UserService userService;
    @Autowired
    private OrderInfoDOMapper orderDOMapper;
    @Autowired
    private SequenceInfoDOMapper sequenceInfoDOMapper;//id自增策略表 表名：sequence_info

    @Autowired
    private StockLogDOMapper stockLogDOMapper;


    @Override
    @Transactional
    public OrderModel createOrder(Integer userId, Integer promoId, Integer itemId, Integer amount,String stockLogId) throws BusinessException {
        //1.校验下单状态，下单的商品是否存在，用户是否合法，购买数量是否正确
        //校验商品是否存在
        //todo 走缓存
        //ItemModel itemModelById = itemService.getItemById(itemId);
        ItemModel itemModelById = itemService.getItemWithCacheById(itemId);
        if (itemModelById == null) {
            throw new BusinessException(EmBusinessError.PARMATER_VALIDATION_ERROR, "商品不存在");
        }
        /* 使用了秒杀名牌，在秒杀令牌的方法里，已经校验了商品、用户的信息。

        //校验用户是否存在
        //todo 走缓存
        //UserModel userById = userService.getUserById(userId);
        UserModel userById = userService.getUserByIdInCache(userId);
        if (userById == null) {
            throw new BusinessException(EmBusinessError.PARMATER_VALIDATION_ERROR, "用户不存在");
        }

         */

        //校验购买数量是否正确
        if (amount <= 0 || amount > 99) {
            throw new BusinessException(EmBusinessError.PARMATER_VALIDATION_ERROR, "购买数量不正确");
        }
        //校验活动信息
        if (promoId != null) {
            //1.校验对应活动是否存在对应的商品
            if (promoId != itemModelById.getPromoModel().getId()) {
                throw new BusinessException(EmBusinessError.PARMATER_VALIDATION_ERROR, "活动信息不正确");
            }
            //2校验活动是否正在进行中
            else if (itemModelById.getPromoModel().getStatus() != 2) {
                throw new BusinessException(EmBusinessError.PARMATER_VALIDATION_ERROR, "活动信息还未开始");
            }
        }




        //2.落单减库存
        //减数据库中的缓存
        //boolean decreaseResult = itemService.decreaseStock(itemId, amount);
        //todo 减redis中的缓存
        boolean decreaseResult = itemService.decreaseStockInCache(itemId, amount);
        if (!decreaseResult) {
            //扣减失败
            throw new BusinessException(EmBusinessError.STOCK_NOT_ENOUGH);
        }
        //3.订单入库
        OrderModel orderModel = new OrderModel();
        orderModel.setUserId(userId);
        orderModel.setItemId(itemId);
        if (promoId != null) {
            orderModel.setItemPrice(itemModelById.getPromoModel().getPromoItemPrice());
        } else {
            orderModel.setItemPrice(itemModelById.getPrice());
            promoId = 0;//没活动
        }
        orderModel.setPromoId(promoId);
        orderModel.setAmout(amount);
        orderModel.setOrderPrice(itemModelById.getPrice().multiply(new BigDecimal(amount)));
        //生成订单号（流水号）
        orderModel.setId(this.generateOrderNo());

        OrderInfoDO orderDO = this.covertOrderDOFromModel(orderModel);
        //插入一条订单
        orderDOMapper.insert(orderDO);

        //增加商品销量
        itemService.increaseSales(itemId, amount);

//        //todo 最后异步扣减数据库库存
//        boolean mqResult = itemService.asyncDecreaseStock(itemId, amount);
//        //如果异步扣减数据库库存消息发送失败 需要回滚redis库存扣减
//        if (!mqResult){
//            itemService.increaseStock(itemId,amount);
//            //抛出异常，使整个创建订单的方法回滚，让本次交易失败
//            throw new BusinessException(EmBusinessError.MQ_SEND_FAIL);
//        }

        //todo 设置库存流水状态，如果一个下单操作能够走到这一步，那么设置库存的流水状态为 成功！
        StockLogDO stockLogDO = stockLogDOMapper.selectByPrimaryKey(stockLogId);
        if (stockLogDO==null){
            throw new BusinessException(EmBusinessError.UNKONMW_ERROR);
        }
        //设置库存流水状态为 下单成功
        stockLogDO.setStatus(2);
        stockLogDOMapper.updateByPrimaryKeySelective(stockLogDO);

        return orderModel;
    }

    /**
     * generateOrderNo()方法是用于生成订单id的一个方法，方法内需要调用数据库查询序列值
     * 并且最终是被上面的createOrder()方法所调用，如果createOrder()方法发生回滚，那么自然而然generateOrderNo()方法
     * 也会发生回滚，因为订单信息没有插入到数据库中，那么该自增序列也不会增加！
     * 为了保证订单id的全局唯一性，虽然当前的自增序列没有被使用，但是我们还是需要让它在原来的基础上进行自增。
     * 但是createOrder()方法是被@Transactional所控制的，自然而然generateOrderNo()方法跟它属于同一个事务。
     * 所以，我们需要给generateOrderNo()单独创建一个事务，使用 Propagation.REQUIRES_NEW 创建一个新的事务！！
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public String generateOrderNo() {
        //订单号有16位
        StringBuilder builder = new StringBuilder();
        //1.前8位为时间信息 年月日
        LocalDateTime time = LocalDateTime.now();
        String nowTime = time.format(DateTimeFormatter.ISO_DATE).replace("-", "");
        builder.append(nowTime);
        //2.中间6位为自增序列
        //获取当前的sequence(序列)
        int sequence = 0;
        SequenceInfoDO orderInfo = sequenceInfoDOMapper.getSequenceByName("order_info");
        sequence = orderInfo.getCurrentValue();
        //增加序列值
        orderInfo.setCurrentValue(orderInfo.getStep() + orderInfo.getCurrentValue());
        sequenceInfoDOMapper.updateByPrimaryKeySelective(orderInfo);
        //不足6位用0补齐
        String sequenceStr = String.valueOf(sequence);
        for (int i = 0; i < 6 - sequenceStr.length(); i++) {
            builder.append(0);
        }
        builder.append(sequence);
        //3.最后两位为分库分表位，目前暂时写死为00
        builder.append("00");

        return builder.toString();
    }

    //model-->do
    private OrderInfoDO covertOrderDOFromModel(OrderModel orderModel) {
        if (orderModel == null) {
            return null;
        }
        OrderInfoDO orderDO = new OrderInfoDO();
        BeanUtil.copyProperties(orderModel, orderDO);
        return orderDO;
    }


}
